<template>
  <div class="scene">
    <canvas id="three"></canvas>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' // 轨道控制器

/**
 * onMounted 钩子函数
 */
onMounted(() => {
    /**
     * 场景 scene
     */
    const scene = new THREE.Scene()
    scene.background = new THREE.Color('#00DCFF')

    /**
     * 相机 camera
     */
    const camera = new THREE.PerspectiveCamera(
      50, // 透视摄像机，垂直视野角度
      window.innerWidth / window.innerHeight, // 摄像机横纵比
      10, // 近端面
      12000 // 远端面
    )

    // 手动调整摄像机位置
    camera.position.x = 600
    camera.position.z = 2400
    camera.position.y = 480

    /**
     * 渲染器 renderer
     */
    const canvas = document.querySelector('#three') // 将 canvas 绑定到元素上
    const renderer = new THREE.WebGLRenderer({
      canvas,
      antialias: true // 抗锯齿
    })

    /**
     * 导入 gltf 格式的模型
     */
    const gltfLoader = new GLTFLoader()
    gltfLoader.load('./static/models/scene.gltf', (gltf) => {
      const model = gltf.scene
      scene.add(model)
    })

    /**
     * 加个坐标轴
     */
    var axes = new THREE.AxesHelper(1000)
    scene.add(axes)

    /**
     * 加个地面
     */
    const plane = createPlane()
    scene.add(plane)
    
    function createPlane() {
      var textureLoader = new THREE.TextureLoader();
      var cubeMaterial = new THREE.MeshStandardMaterial({
        map: textureLoader.load("static/textures/cd.jpeg"),
      });
      cubeMaterial.map.wrapS = THREE.RepeatWrapping;
      cubeMaterial.map.wrapT = THREE.RepeatWrapping;
      cubeMaterial.map.repeat.set(8, 8)
      // 创建地平面并设置大小
      var planeGeometry = new THREE.PlaneGeometry(3000, 3000);
      var plane = new THREE.Mesh(planeGeometry, cubeMaterial);

      // 设置平面位置并旋转
      plane.rotation.x = -0.5 * Math.PI;
      plane.position.x = 0;
      plane.position.y = -200;
      plane.position.z = 0;
      return plane
    }

    /**
     * 轨道控制器
     */
    // const controls = new OrbitControls(camera, renderer.domElement); // 传入 将要被控制的相机 / 用于事件监听的HTML元素
    // controls.enableDamping = true // 启用惯性
    // controls.autoRotate = true // 自动旋转
    // controls.autoRotateSpeed = 5.0 // 自动旋转
    // controls.maxPolarAngle = Math.PI * 0.5 // 相机旋转最大角度，不会超过地面
    // controls.enabled = false // 当设置为false时，控制器将不会响应用户的操作

    /**
     * 天空盒效果
     */
    // const path = './static/img/' // 设置路径
    // const directions = ['px', 'nx', 'py', 'ny', 'pz', 'nz'] // 贴图名称, 左右、上下、后前
    // const format = '.jpg' // 图片格式

    // // 1、创建盒子，并设置盒子的大小
    // const skyGeometry = new THREE.BoxGeometry( 10000, 10000, 10000 );

    // // 2、导入图片，创建材质
    // const skyMaterial = [];
    // directions.forEach((item) => {
    //   // 加载纹理
    //   const texture = new THREE.TextureLoader().load(path + item + format)
    //   // 传入加载的纹理，创建材质
    //   skyMaterial.push(new THREE.MeshBasicMaterial({ map: texture }))
    // })

    // const skyBox = new THREE.Mesh(skyGeometry, skyMaterial) // 传入盒子+材质
    // skyBox.geometry.scale(1, 1, -1) // 贴图反转
    // scene.add( skyBox );

    /**
     * 全景图片贴图
     */
    // const url = 'https://cdn.huodao.hk/upload_img/20220621/6bd594e62ea5654c03d7b82718443751.png?proportion=1.99'
    // const geometry = new THREE.SphereGeometry(5000, 32, 32)
    // const texture = new THREE.TextureLoader().load(url)
    // const material = new THREE.MeshBasicMaterial({ map: texture })
    // const sphere = new THREE.Mesh(geometry, material)
    // sphere.geometry.scale(1, 1, -1)
    // scene.add(sphere)

    /**
     * 动态跟踪
     */
    let curve
    // 通过类 CatmullRomCurve3 创建一个3D样条曲线
    curve = new THREE.CatmullRomCurve3([
      new THREE.Vector3(-1000, -5000, -5000),
      new THREE.Vector3(1000, -5000, 0),
      new THREE.Vector3(1800, 800, 1000),
      new THREE.Vector3(800, 5000, 5000),
      new THREE.Vector3(0, 0, 10000)
    ]);
    // 样条曲线均匀分割100
    var points = curve.getPoints(100);
    var geometry = new THREE.BufferGeometry();
    // 把从曲线轨迹上获得的顶点坐标赋值给几何体
    geometry.vertices = points
    var material = new THREE.LineBasicMaterial({
      color: "#4488ff"
    });
    var line = new THREE.Line(geometry, material);
    scene.add(line);
    
    //管道体
    const tubeGeometry = new THREE.TubeGeometry(curve, 100, 500, 30); // 101取点数 3为r 30为三角切面数
    const tubeMesh = new THREE.Mesh(tubeGeometry, new THREE.MeshBasicMaterial({
      color: "#00aa00",
      side: THREE.DoubleSide,
      wireframe: true
    }))
    scene.add(tubeMesh)

    // 引用GLTFLoader.js文件，加载模型
    let model
    gltfLoader.load('./static/models/scene2.gltf', (gltf) => {
      model = gltf.scene
      model.position.set(-1000, -5000, -5000);
      model.scale.set(200, 200, 200)
      scene.add(model)
    })

    function changeLookAt(t) {
      // 当前点在线条上的位置
      const position = curve.getPointAt(t);
      var nPos = new THREE.Vector3(position.x, position.y - 100, position.z);
      model.position.copy(nPos);
      // 返回点t在曲线上位置切线向量
      const tangent = curve.getTangentAt(t);
      // 位置向量和切线向量相加即为所需朝向的点向量
      const lookAtVec = tangent.add(nPos);
      model.lookAt(lookAtVec);
    
      if (t > 0.03) {
        var pos = curve.getPointAt(t - 0.03);
        camera.position.copy(pos);
        camera.lookAt(position)
      }
    }
     
    /**
     * 循环渲染每一帧
     */
    function animate() {
      renderer.render(scene, camera) // 刷新渲染器
      // controls.update(); // 刷新轨道控制器
      requestAnimationFrame(animate)

      if (curve) {
        const time = Date.now();
        const loopTime = 10 * 1000; // loopTime: 循环一圈的时间
        let t = (time % loopTime) / loopTime; // 计算当前时间进度百分比
        setTimeout(function() {
          changeLookAt(t);
        }, 2000)
      }
    }
    animate()

    /**
     *  添加光源
     */
    // 添加个平行光
    const dirLight = new THREE.DirectionalLight(0xffffff, 1);
    // 光源等位置
    dirLight.position.set(10, 10, 10);
    // 可以产生阴影
    dirLight.castShadow = true;
    dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
    scene.add(dirLight);
    
    // 添加个半球光
    const hemLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6);
    hemLight.position.set(0, 100, 0);
    scene.add(hemLight);

    /**
     * 调整设备 物理像素分辨率与 CSS 像素分辨率的比值
     */
    function resizeRendererToDisplaySize(renderer) {
      const canvas = renderer.domElement;
      var width = window.innerWidth;
      var height = window.innerHeight;
      var canvasPixelWidth = canvas.width / window.devicePixelRatio;
      var canvasPixelHeight = canvas.height / window.devicePixelRatio;
    
      const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height
      if (needResize) {
        renderer.setSize(width, height, false)
      }
      return needResize
    }
    
    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement
      camera.aspect = canvas.clientWidth / canvas.clientHeight
      camera.updateProjectionMatrix()
    }
})
</script>

<style lang="less" scoped>
    .scene {
        width: 100%;
        height: calc( 100vh - 76px );
        overflow: hidden;
        canvas {
            width: 100%;
            height: 100%;
        }
    }
</style>>